package org.gaaidou.ptarmigan.springboot.distributed.redis;

import lombok.extern.slf4j.Slf4j;
import org.gaaidou.ptarmigan.springboot.distributed.WorkerIdService;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import java.time.Duration;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

@Component
@Slf4j
public class RedisWorkerIdService implements WorkerIdService {
    private final int workId;

    public RedisWorkerIdService(@Autowired RedissonClient redissonClient, @Autowired ApplicationContext applicationContext) {
        workId = newWorkId(redissonClient, applicationContext);

        Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(() -> {
            for (int retryIndex = 0; retryIndex < 3; retryIndex++) {
                if (renew(key(workId, applicationContext.getApplicationName()), redissonClient)) {
                    break;
                }
            }
        }, 0, 1, TimeUnit.HOURS);
    }

    @Autowired
    public Integer workId() {
        return workId;
    }

    private int newWorkId(RedissonClient redissonClient, ApplicationContext applicationContext) {
        for (int workIdIndex = 0; workIdIndex < 1024; workIdIndex++) {
            if (redissonClient.getBucket(key(workIdIndex, applicationContext.getApplicationName()))
                    .setIfAbsent(Boolean.TRUE, Duration.ofDays(1))) {
                return workIdIndex;
            }
        }

        throw new IllegalStateException("the worker node is overload");
    }

    private boolean renew(String key, RedissonClient redissonClient) {
        log.info("renew redis key of worker's id: {}", key);
        return redissonClient.getBucket(key).expireIfSet(Duration.ofDays(1));
    }

    private String key(int workIdIndex, String appName) {
        return String.format("work_id:%s:%s", appName, workIdIndex);
    }

}
